網頁應用程式5

林嶔 (Lin, Chin)

Lesson 16

第一節:自己創造互動式圖形(1)

  1. 自由度相當低,只能畫幾種的圖形

  2. 超大型數據在html格式下會用掉非常多資源

– ui.R

library(shiny)

fluidPage(
  fluidRow(
    column(width = 4,
           plotOutput("plot1", height = 350,
                      click = "plot_click",
                      dblclick = dblclickOpts(id = "plot_dblclick"),
                      hover = hoverOpts(id = "plot_hover"),
                      brush = brushOpts(id = "plot_brush")
           )
    )
  ),
  fluidRow(
    column(width = 3,
           verbatimTextOutput("click_info")
    ),
    column(width = 3,
           verbatimTextOutput("dblclick_info")
    ),
    column(width = 3,
           verbatimTextOutput("hover_info")
    ),
    column(width = 3,
           verbatimTextOutput("brush_info")
    )
  )
)

– server.R

library(shiny)

data(cars)
dat = cars

shinyServer(function(input, output) {
  output$plot1 <- renderPlot({
    plot(dat)
  })
  
  output$click_info <- renderPrint({
    cat("input$plot_click:\n")
    str(input$plot_click)
  })
  output$hover_info <- renderPrint({
    cat("input$plot_hover:\n")
    str(input$plot_hover)
  })
  output$dblclick_info <- renderPrint({
    cat("input$plot_dblclick:\n")
    str(input$plot_dblclick)
  })
  output$brush_info <- renderPrint({
    cat("input$plot_brush:\n")
    str(input$plot_brush)
  })
  
})

第一節:自己創造互動式圖形(2)

– 這邊需要用到兩個新函數:reactiveValues()、observe()和observeEvent()

library(shiny)

fluidPage(
  fluidRow(
    column(width = 4,
           plotOutput("plot1", height = 400,
                      brush = brushOpts(id = "plot1_brush", resetOnNew = TRUE))
    ),
    column(width = 4,
           plotOutput("plot2", height = 400)
    ),
    column(width = 4,
           plotOutput("plot3", height = 400,
                      dblclick = "plot3_dblclick",
                      brush = brushOpts(id = "plot3_brush", resetOnNew = TRUE))
    )
  )
)
library(shiny)

data(cars)
dat = cars

shinyServer(function(input, output) {
  
  ranges1 = reactiveValues(x = NULL, y = NULL)
  
  observe({
    brush1 = input$plot1_brush
    if (!is.null(brush1)) {
      ranges1$x = c(brush1$xmin, brush1$xmax)
      ranges1$y = c(brush1$ymin, brush1$ymax)
    } else {
      ranges1$x = NULL
      ranges1$y = NULL
    }
  })
  
  output$plot1 <- renderPlot({
    plot(dat)
  })
  
  output$plot2 <- renderPlot({
    plot(dat, xlim = ranges1$x, ylim = ranges1$y)
  })
  
  ranges2 <- reactiveValues(x = NULL, y = NULL)
  
  output$plot3 <- renderPlot({
    plot(dat, xlim = ranges2$x, ylim = ranges2$y)
  })
  
  observeEvent(input$plot3_dblclick, {
    brush2 <- input$plot3_brush
    if (!is.null(brush2)) {
      ranges2$x <- c(brush2$xmin, brush2$xmax)
      ranges2$y <- c(brush2$ymin, brush2$ymax)
    } else {
      ranges2$x <- NULL
      ranges2$y <- NULL
    }
  })
  
})

練習-1

– 我們先看看裡面的一個文字檔案,而這個檔案描述的是5張圖的人類位置在哪:

box_info = read.csv("examples/label.csv", header = TRUE, stringsAsFactors = FALSE)
box_info
##    obj_name   col_left col_right   row_bot   row_top prob img_id
## 1    person 0.60728125 0.7782344 0.8139110 0.1637471    1      1
## 2    person 0.00000000 0.0971250 0.7015925 0.6154801    1      1
## 3    person 0.50981250 0.6211250 0.8687150 0.4078505    1      2
## 4    person 0.01529687 0.2058281 0.9194159 0.3903271    1      2
## 5    person 0.79756250 0.9907812 0.9042757 0.4001636    1      2
## 6    person 0.32854688 0.6720156 0.8738333 0.2985208    1      3
## 7    person 0.88721875 0.9362500 0.7515368 0.5911255    1      4
## 8    person 0.39248437 0.4289219 0.3639394 0.2303463    1      4
## 9    person 0.47934375 0.4961250 0.6005000 0.5788542    1      5
## 10   person 0.76668750 0.7721250 0.5681875 0.5610833    1      5

練習-1

library(jpeg)
library(imager)

Show_img = function (img, box_info = NULL, col_bbox = '#FFFFFF00', col_label = '#FF0000FF') {
  
  par(mar = rep(0, 4))
  plot(NA, xlim = c(0, 1), ylim = c(1, 0), xaxt = "n", yaxt = "n", bty = "n")
  img = (img - min(img))/(max(img) - min(img))
  img = as.raster(img)
  rasterImage(img, 0, 1, 1, 0, interpolate=FALSE)
  
  
  if (!is.null(box_info)) {
    if (nrow(box_info) > 0) {
      for (i in 1:nrow(box_info)) {
        size = max(box_info[i,3] - box_info[i,2], 0.2)
        rect(xleft = box_info[i,2], xright = box_info[i,2] + 0.06*sqrt(size)*nchar(box_info[i,1]),
             ybottom = box_info[i,5] + 0.08*sqrt(size), ytop = box_info[i,5],
             col = col_label, border = col_label, lwd = 0)
        text(x = box_info[i,2] + 0.03*sqrt(size) * nchar(box_info[i,1]),
             y = box_info[i,5] + 0.04*sqrt(size),
             labels = box_info[i,1],
             col = 'white', cex = 1.5*sqrt(size), font = 2)
        rect(xleft = box_info[i,2], xright = box_info[i,3],
             ybottom = box_info[i,4], ytop = box_info[i,5],
             col = col_bbox, border = col_label, lwd = 5*sqrt(size))
      }
    }
  }
  
}
img = readJPEG("examples/2.jpeg")
Show_img(img, box_info[box_info[,"img_id"] == 2,])

img = readJPEG("examples/3.jpeg")
Show_img(img, box_info[box_info[,"img_id"] == 3,])

練習-1

  1. 讓使用者能夠自己上傳一張圖片上去

  2. 框出物件的位置在哪,並選擇框選的物件為何(目前只有人類供選擇)

  3. 按下按鍵後紀錄框的位置

  4. 將資訊記錄在資料表內,而img_id設定為圖像的檔名

  5. 如果使用者覺得框錯了,可以把它刪除

  6. 使用者最終能下載該資料表

練習-1 答案

library(shiny)
library(DT)
library(jpeg)
library(imager)

fluidPage(
  fluidRow(
    column(width = 4,
           fileInput("files", label = h4("Upload your jpeg image:"), multiple = FALSE, accept = "image/jpeg"),
           br(),
           radioButtons("obj", label = h4("Please select a object name:"), c("person" = "person")),
           br(),
           downloadButton("download", label = "Download file", class = NULL)
    ),
    column(width = 7,
           plotOutput("plot", height = 416, width = 416,
                      dblclick = "plot_dblclick",
                      brush = brushOpts(id = "plot_brush", resetOnNew = TRUE)),
           br(),
           actionButton("delete", strong("Delete selected box!"), icon("list-alt")),
           br(),
           br(),
           DT::dataTableOutput('table')
    )
  )
)
library(shiny)
library(DT)
library(jpeg)
library(imager)

Show_img = function (img, box_info = NULL, col_bbox = '#FFFFFF00', col_label = '#FF0000FF') {
  
  par(mar = rep(0, 4))
  plot(NA, xlim = c(0, 1), ylim = c(1, 0), xaxt = "n", yaxt = "n", bty = "n")
  img = (img - min(img))/(max(img) - min(img))
  img = as.raster(img)
  rasterImage(img, 0, 1, 1, 0, interpolate=FALSE)
  
  
  if (!is.null(box_info)) {
    if (nrow(box_info) > 0) {
      for (i in 1:nrow(box_info)) {
        size = max(box_info[i,3] - box_info[i,2], 0.2)
        rect(xleft = box_info[i,2], xright = box_info[i,2] + 0.06*sqrt(size)*nchar(box_info[i,1]),
             ybottom = box_info[i,5] + 0.08*sqrt(size), ytop = box_info[i,5],
             col = col_label, border = col_label, lwd = 0)
        text(x = box_info[i,2] + 0.03*sqrt(size) * nchar(box_info[i,1]),
             y = box_info[i,5] + 0.04*sqrt(size),
             labels = box_info[i,1],
             col = 'white', cex = 1.5*sqrt(size), font = 2)
        rect(xleft = box_info[i,2], xright = box_info[i,3],
             ybottom = box_info[i,4], ytop = box_info[i,5],
             col = col_bbox, border = col_label, lwd = 5*sqrt(size))
      }
    }
  }
  
}

shinyServer(function(input, output) {
  
  IMAGE = reactive({
    if (is.null(input$files)) {return()} else {
      img = readJPEG(input$files$datapath)
      return(img) 
    }
  })
  
  MY_TABLE = reactiveValues(table = NULL)
  
  output$plot = renderPlot({
    img = IMAGE()
    if (!is.null(input$files$name)) {
      box_info = MY_TABLE$table
      box_info = box_info[box_info[,"img_id"] == input$files$name,]
    } else {
      box_info = NULL
    }
    if (is.null(img)) {return()} else {
      Show_img(img = img, box_info = box_info)
    }
  })
  
  observeEvent(input$plot_dblclick, {
    brush = input$plot_brush
    if (!is.null(brush) & !is.null(input$files$name)) {
      new_table = data.frame(obj_name = input$obj,
                             col_left = brush$xmin,
                             col_right = brush$xmax,
                             row_bot = brush$ymax,
                             row_top = brush$ymin,
                             prob = 1,
                             img_id = input$files$name,
                             stringsAsFactors = FALSE)
      MY_TABLE$table = rbind(MY_TABLE$table, new_table)
    }
  })
  
  observeEvent(input$delete, {
    selection = as.numeric(input$table_rows_selected)
    if (length(selection)!=0) {
      MY_TABLE$table = MY_TABLE$table[-selection,]
    }
  })
  
  output$table = DT::renderDataTable({
    dat = MY_TABLE$table
    if (is.null(dat)) {return()} else {
      dat[,2] = round(dat[,2], 3)
      dat[,3] = round(dat[,3], 3)
      dat[,4] = round(dat[,4], 3)
      dat[,5] = round(dat[,5], 3)
      Result = DT::datatable(dat)
      return(Result)
    }
  })
  
  output$download = downloadHandler(
    filename = function() {'label.csv'},
    content = function(con) {
      dat = MY_TABLE$table
      if (is.null(dat)) {return()} else {
        write.csv(dat, con, row.names = FALSE)
      }
    }
  )
  
  
})

第二節:學習如何套用別人寫好的程式(1)

– 你應該有注意到你的App是沒有辦法用帳號密碼保護的,而要做這件事情確實是有難度,畢竟我們似乎是沒有學過兩個頁面的切換功能,那讓我們再google看看吧:

F16_5

– 其中的第三個討論串:Starting Shiny app after password input就是在講這件事情

F16_6

第二節:學習如何套用別人寫好的程式(2)

rm(list = ls())
library(shiny)

Logged = FALSE;
my_username <- "test"
my_password <- "test"

ui1 <- function(){
  tagList(
    div(id = "login",
        wellPanel(textInput("userName", "Username"),
                  passwordInput("passwd", "Password"),
                  br(),actionButton("Login", "Log in"))),
    tags$style(type="text/css", "#login {font-size:10px;   text-align: left;position:absolute;top: 40%;left: 50%;margin-top: -100px;margin-left: -150px;}")
  )}

ui2 <- function(){tagList(tabPanel("Test"))}

ui = (htmlOutput("page"))
server = (function(input, output,session) {

  USER <- reactiveValues(Logged = Logged)

  observe({ 
    if (USER$Logged == FALSE) {
      if (!is.null(input$Login)) {
        if (input$Login > 0) {
          Username <- isolate(input$userName)
          Password <- isolate(input$passwd)
          Id.username <- which(my_username == Username)
          Id.password <- which(my_password == Password)
          if (length(Id.username) > 0 & length(Id.password) > 0) {
            if (Id.username == Id.password) {
              USER$Logged <- TRUE
            } 
          }
        } 
      }
    }    
  })
  observe({
    if (USER$Logged == FALSE) {

      output$page <- renderUI({
        div(class="outer",do.call(bootstrapPage,c("",ui1())))
      })
    }
    if (USER$Logged == TRUE) 
    {
      output$page <- renderUI({
        div(class="outer",do.call(navbarPage,c(inverse=TRUE,title = "Contratulations you got in!",ui2())))
      })
      print(ui)
    }
  })
})

#runApp(list(ui = ui, server = server))

第二節:學習如何套用別人寫好的程式(3)

– global.R

library(shiny)

Logged = FALSE;
my_username <- "test"
my_password <- "test"

ui1 <- function(){
  tagList(
    div(id = "login",
        wellPanel(textInput("userName", "Username"),
                  passwordInput("passwd", "Password"),
                  br(),actionButton("Login", "Log in"))),
    tags$style(type="text/css", "#login {font-size:10px;   text-align: left;position:absolute;top: 40%;left: 50%;margin-top: -100px;margin-left: -150px;}")
  )}

ui2 <- function(){tagList(tabPanel("Test"))}

– ui.R

library(shiny)

htmlOutput("page")
library(shiny)

function(input, output,session) {

  USER <- reactiveValues(Logged = Logged)

  observe({ 
    if (USER$Logged == FALSE) {
      if (!is.null(input$Login)) {
        if (input$Login > 0) {
          Username <- isolate(input$userName)
          Password <- isolate(input$passwd)
          Id.username <- which(my_username == Username)
          Id.password <- which(my_password == Password)
          if (length(Id.username) > 0 & length(Id.password) > 0) {
            if (Id.username == Id.password) {
              USER$Logged <- TRUE
            } 
          }
        } 
      }
    }    
  })
  observe({
    if (USER$Logged == FALSE) {

      output$page <- renderUI({
        div(class="outer",do.call(bootstrapPage,c("",ui1())))
      })
    }
    if (USER$Logged == TRUE) 
    {
      output$page <- renderUI({
        div(class="outer",do.call(navbarPage,c(inverse=TRUE,title = "Contratulations you got in!",ui2())))
      })
      print(ui)
    }
  })
}

第二節:學習如何套用別人寫好的程式(4)

– global.R

library(shiny)

Logged = FALSE;
my_username <- "test"
my_password <- "test"

ui1 <- function(){
  tagList(
    div(id = "login",
        wellPanel(textInput("userName", "Username"),
                  passwordInput("passwd", "Password"),
                  br(),actionButton("Login", "Log in"))),
    tags$style(type="text/css", "#login {font-size:10px;   text-align: left;position:absolute;top: 40%;left: 50%;margin-top: -100px;margin-left: -150px;}")
  )}

ui2 <- function(){tagList(tabPanel("Test",
                                   sliderInput("obs", "Number of observations:", min = 0, max = 1000, value = 500),
                                   plotOutput("distPlot")))}

– ui.R

library(shiny)

htmlOutput("page")
library(shiny)

function(input, output,session) {
  
  USER <- reactiveValues(Logged = Logged)
  
  observe({ 
    if (USER$Logged == FALSE) {
      if (!is.null(input$Login)) {
        if (input$Login > 0) {
          Username <- isolate(input$userName)
          Password <- isolate(input$passwd)
          Id.username <- which(my_username == Username)
          Id.password <- which(my_password == Password)
          if (length(Id.username) > 0 & length(Id.password) > 0) {
            if (Id.username == Id.password) {
              USER$Logged <- TRUE
            } 
          }
        } 
      }
    }    
  })
  observe({
    if (USER$Logged == FALSE) {
      
      output$page <- renderUI({
        div(class="outer",do.call(bootstrapPage,c("",ui1())))
      })
    }
    if (USER$Logged == TRUE) 
    {
      output$page <- renderUI({
        div(class="outer",do.call(navbarPage,c(inverse=TRUE,title = "Contratulations you got in!",ui2())))
      })
      print(ui)
      
      output$distPlot = renderPlot({
        
        # generate an rnorm distribution and plot it
        dist = rnorm(input$obs)
        hist(dist)
      })
      
    }
  })
}

練習-2

– 請你設計兩個頁面的程序,讓使用者在使用標註系統之前需要輸入帳號密碼!

練習-2 答案

library(shiny)
library(DT)
library(jpeg)
library(imager)

Logged = FALSE;
my_username <- "test"
my_password <- "test"

Show_img = function (img, box_info = NULL, col_bbox = '#FFFFFF00', col_label = '#FF0000FF') {
  
  par(mar = rep(0, 4))
  plot(NA, xlim = c(0, 1), ylim = c(1, 0), xaxt = "n", yaxt = "n", bty = "n")
  img = (img - min(img))/(max(img) - min(img))
  img = as.raster(img)
  rasterImage(img, 0, 1, 1, 0, interpolate=FALSE)
  
  
  if (!is.null(box_info)) {
    if (nrow(box_info) > 0) {
      for (i in 1:nrow(box_info)) {
        size = max(box_info[i,3] - box_info[i,2], 0.2)
        rect(xleft = box_info[i,2], xright = box_info[i,2] + 0.06*sqrt(size)*nchar(box_info[i,1]),
             ybottom = box_info[i,5] + 0.08*sqrt(size), ytop = box_info[i,5],
             col = col_label, border = col_label, lwd = 0)
        text(x = box_info[i,2] + 0.03*sqrt(size) * nchar(box_info[i,1]),
             y = box_info[i,5] + 0.04*sqrt(size),
             labels = box_info[i,1],
             col = 'white', cex = 1.5*sqrt(size), font = 2)
        rect(xleft = box_info[i,2], xright = box_info[i,3],
             ybottom = box_info[i,4], ytop = box_info[i,5],
             col = col_bbox, border = col_label, lwd = 5*sqrt(size))
      }
    }
  }
  
}

ui1 <- function(){
  tagList(
    div(id = "login",
        wellPanel(textInput("userName", "Username"),
                  passwordInput("passwd", "Password"),
                  br(),actionButton("Login", "Log in"))),
    tags$style(type="text/css", "#login {font-size:10px;   text-align: left;position:absolute;top: 40%;left: 50%;margin-top: -100px;margin-left: -150px;}")
  )}

ui2 <- function(){tagList(tabPanel("Main page",
                                   fluidRow(
                                     column(width = 4,
                                            fileInput("files", label = h4("Upload your jpeg image:"), multiple = FALSE, accept = "image/jpeg"),
                                            br(),
                                            radioButtons("obj", label = h4("Please select a object name:"), c("person" = "person")),
                                            br(),
                                            downloadButton("download", label = "Download file", class = NULL)
                                     ),
                                     column(width = 7,
                                            plotOutput("plot", height = 416, width = 416,
                                                       dblclick = "plot_dblclick",
                                                       brush = brushOpts(id = "plot_brush", resetOnNew = TRUE)),
                                            br(),
                                            actionButton("delete", strong("Delete selected box!"), icon("list-alt")),
                                            br(),
                                            br(),
                                            DT::dataTableOutput('table')
                                     )
                                   )))}
library(shiny)
library(DT)
library(jpeg)
library(imager)

htmlOutput("page")
library(shiny)
library(DT)
library(jpeg)
library(imager)

function(input, output,session) {
  
  USER <- reactiveValues(Logged = Logged)
  
  observe({ 
    if (USER$Logged == FALSE) {
      if (!is.null(input$Login)) {
        if (input$Login > 0) {
          Username <- isolate(input$userName)
          Password <- isolate(input$passwd)
          Id.username <- which(my_username == Username)
          Id.password <- which(my_password == Password)
          if (length(Id.username) > 0 & length(Id.password) > 0) {
            if (Id.username == Id.password) {
              USER$Logged <- TRUE
            } 
          }
        } 
      }
    }    
  })
  observe({
    if (USER$Logged == FALSE) {
      
      output$page <- renderUI({
        div(class="outer",do.call(bootstrapPage,c("",ui1())))
      })
    }
    if (USER$Logged == TRUE) 
    {
      output$page <- renderUI({
        div(class="outer",do.call(navbarPage,c(inverse=TRUE,title = "Contratulations you got in!",ui2())))
      })
      print(ui)
      
      IMAGE = reactive({
        if (is.null(input$files)) {return()} else {
          img = readJPEG(input$files$datapath)
          return(img) 
        }
      })
      
      MY_TABLE = reactiveValues(table = NULL)
      
      output$plot = renderPlot({
        img = IMAGE()
        if (!is.null(input$files$name)) {
          box_info = MY_TABLE$table
          box_info = box_info[box_info[,"img_id"] == input$files$name,]
        } else {
          box_info = NULL
        }
        if (is.null(img)) {return()} else {
          Show_img(img = img, box_info = box_info)
        }
      })
      
      observeEvent(input$plot_dblclick, {
        brush = input$plot_brush
        if (!is.null(brush) & !is.null(input$files$name)) {
          new_table = data.frame(obj_name = input$obj,
                                 col_left = brush$xmin,
                                 col_right = brush$xmax,
                                 row_bot = brush$ymax,
                                 row_top = brush$ymin,
                                 prob = 1,
                                 img_id = input$files$name,
                                 stringsAsFactors = FALSE)
          MY_TABLE$table = rbind(MY_TABLE$table, new_table)
        }
      })
      
      observeEvent(input$delete, {
        selection = as.numeric(input$table_rows_selected)
        if (length(selection)!=0) {
          MY_TABLE$table = MY_TABLE$table[-selection,]
        }
      })
      
      output$table = DT::renderDataTable({
        dat = MY_TABLE$table
        if (is.null(dat)) {return()} else {
          dat[,2] = round(dat[,2], 3)
          dat[,3] = round(dat[,3], 3)
          dat[,4] = round(dat[,4], 3)
          dat[,5] = round(dat[,5], 3)
          Result = DT::datatable(dat)
          return(Result)
        }
      })
      
      output$download = downloadHandler(
        filename = function() {'label.csv'},
        content = function(con) {
          dat = MY_TABLE$table
          if (is.null(dat)) {return()} else {
            write.csv(dat, con, row.names = FALSE)
          }
        }
      )
      
    }
  })
}

第三節:Case study-KAMERA data(1)

第三節:Case study-KAMERA data(2)

– 請按這裡下載訓練資料

– 請按這裡下載答案提交格式

data.train=read.csv("kamera.csv",header=TRUE) #讀取kamera.csv,並存成"Data"這個物件(資料表格式)
head(data.train,5) #看"Data"這個資料表的前5個row
##         date    tz Hospital_PK Level   PDR       PBR total A01 A02 A03 A04
## 1 2013-01-01 [0,4)           6     1  3.00 0.2857143     0   4   1   1   0
## 2 2013-01-01 [0,4)          22     1  8.50 0.4722222    51  49   0   2   0
## 3 2013-01-01 [0,4)          35     1 18.75 0.3627451    74  58   8   8   0
## 4 2013-01-01 [0,4)          11     2  8.00 0.6176471    17  17   3   1   0
## 5 2013-01-01 [0,4)          12     2  6.50 0.5200000    13   8   4   1   0
##   A05 A06 A07 A08 A09 A10 A11 A12 A13 A14 A15  A16 A17 C01 C02 C03 C04 C05
## 1   0   0   0   0   0   0   0   1   1   0   0    0   0   0   0   0   0   0
## 2   0   0   2   9  38   2   0   5   5   0  17 2913   0   0   0   0   0   0
## 3   1   0   1  30  43   0   0   9  11   0  39  677   1   3   0   0   0   1
## 4   2   1   0   1   8   8   0   0   0   0   5  384   0   0   0   0   0   0
## 5   0   0   1   0  11   1   0   5   7   0   0    0   0   0   0   3   0   0
##   C06 B01 B02 B03 B04 B05 B06 Light
## 1   0   0   0   0   0   0   0     1
## 2   0   0   5   3   2   0   1     1
## 3   0   0   1   1   6   5   0     3
## 4   0   0   0   8   0   0   0     1
## 5   0   0   0   0   3   1   0     1
data.submit=read.csv("submission.csv",header=TRUE) #讀取submission.csv,並存成"Data"這個物件(資料表格式)
head(data.submit,5) #看"Data"這個資料表的前5個row
##         date    tz Hospital_PK total
## 1 2014-08-11 [0,4)           1     0
## 2 2014-08-11 [0,4)           6     0
## 3 2014-08-11 [0,4)          22     0
## 4 2014-08-11 [0,4)          35     0
## 5 2014-08-11 [0,4)          11     0

第三節:Case study-KAMERA data(3)

– 我們將利用SVM進行預測,並比較兩種資料前處理模式

svm.data = data.frame(x1 = factor(data.train$tz),                       #x1 = 時段
                      x2 = factor(weekdays(as.Date(data.train$date))),  #x2 = 星期[一-日]
                      x3 = factor(data.train$Hospital_PK),              #x3 = 醫院
                      y = data.train$total)                             #y = 急診需求量

prob.train = 0.7                                                        #設定訓練集比例
train_test = sample(c("train","test"), nrow(svm.data),                  #抽樣
                    replace = TRUE, prob = c(prob.train, 1-prob.train))

svm.data.train = svm.data[train_test=="train",]
svm.data.test = svm.data[train_test=="test",]

第三節:Case study-KAMERA data(4)

  1. 直接對y進行預測
  2. 先將y加1後取log再進行預測(因為y為count而包含0,所以要先加1)
library(e1071)
model1 = svm(y~., data = svm.data.train)      #利用data內所有變數對y做預測
pred.y1 = predict(model1,svm.data.test[,1:3]) #使用model1的資訊對測試集做預測
MSE1 = mean((svm.data.test$y - pred.y1)^2)    #取得殘差平方的平均
print(MSE1)
## [1] 279.8662
library(e1071)
svm.data.train$y = log(svm.data.train$y+1)    #對數處理
model2 = svm(y~., data = svm.data.train)      #利用data內所有變數對y做預測
pred.y2 = predict(model2,svm.data.test[,1:3]) #使用model1的資訊對測試集做預測
pred.y2 = exp(pred.y2)-1                      #指數轉換回來
MSE2 = mean((svm.data.test$y - pred.y2)^2)    #取得殘差平方的平均
print(MSE2)
## [1] 113.3869

第三節:Case study-KAMERA data(5)

– 現在我們要用模型1為腳本,寫出一個WebApp來讓使用者能清楚的知道各時間點在這11間醫院的急診需求

– 而聰明的你一定猜的到,等等的練習題就是用模型2為腳本,從而改進我們WebApp的預測能力

library(e1071)
final.model = svm(y~., data = svm.data)              #利用svm.data(原始資料)內所有變數對y做預測
save(final.model, file = "svmmodel.RData")                    #儲存預測模型至svmmodel.RData

rm(final.model)            #移除final.model
load("svmmodel.RData")  #重新載入final.model

#製作predict.data,讓我們能預測每週11家醫院在各時段的急診負荷量
predict.data = data.frame(x1 = factor(data.submit$tz),                        #x1 = 時段
                          x2 = factor(weekdays(as.Date(data.submit$date))),   #x2 = 星期[一-日]
                          x3 = factor(data.submit$Hospital_PK))               #x3 = 醫院
save(predict.data, file = "predict.RData")                 #儲存預測資料的模板至predict.RData

#####################################################################################################
#以下步驟為提交答案所需(如果你也想參加資料挑戰賽)

data.submit[,4] = predict(final.model, predict.data[,1:3]) #使用final.model的資訊對predict.data做預測
write.csv(data.submit, "Finalresult.csv", row.names = FALSE)            #寫出答案,可以上傳對答案
#####################################################################################################

第三節:Case study-KAMERA data(6)

load("svmmodel.RData")  #重新載入final.model
load("predict.RData")  #重新載入predict.data

#自訂欲預測之日期範圍,並產生demo.data
start.date = as.Date("2016-04-07")
end.date = as.Date("2016-04-17")
new.data = data.frame(date = seq(start.date, end.date, by=1))
new.data$x2 = weekdays(new.data$date) 
demo.data = merge(predict.data, new.data, by = "x2")

#使用final.model對demo.data進行預測
demo.data$y = predict(final.model, demo.data[,1:3]) 
head(demo.data)
##       x2    x1 x3       date           y
## 1 星期一 [0,4)  1 2016-04-11  47.3510423
## 2 星期一 [0,4)  6 2016-04-11   0.5547009
## 3 星期一 [0,4) 22 2016-04-11  70.7381905
## 4 星期一 [0,4) 35 2016-04-11 130.4258435
## 5 星期一 [0,4) 11 2016-04-11  22.2921606
## 6 星期一 [0,4) 12 2016-04-11  20.7622100

第三節:Case study-KAMERA data(7)

– 在使用dygraphs套件之前,我們要先對demo.data做一些整理

TimeTable = table(demo.data[,c(4,2)])             #建立時間表格
HosTable = table(demo.data[,3])                 #建立醫院表格
Time.series= paste0(seq(2,22,by=4),":00:00 CST")  #將時段轉換為時間中位數

#產生Total.data儲存結果
Total.data = matrix(NA, ncol = length(HosTable), nrow = length(TimeTable))
colnames(Total.data) = paste0("Hospital",names(HosTable))
rownames(Total.data) = 1:nrow(Total.data)

#使用一個三層迴圈將Total.data填滿
for (i in 1:nrow(TimeTable)) {
  for (j in 1:ncol(TimeTable)) {
    for (k in 1:length(HosTable)) {
      n = demo.data$y[demo.data$date==rownames(TimeTable)[i]&demo.data$x1==colnames(TimeTable)[j]&demo.data$x3==names(HosTable)[k]]
      Total.data[(i-1)*ncol(TimeTable)+j,k] = n
    }
    rownames(Total.data)[(i-1)*ncol(TimeTable)+j] = paste(rownames(TimeTable)[i],Time.series[j],sep=" ")
  }
}

head(Total.data)
##                         Hospital1    Hospital6 Hospital11 Hospital12
## 2016-04-07 2:00:00 CST   45.26475  0.001034993   21.09283   19.46670
## 2016-04-07 6:00:00 CST   47.75544  0.590673023   22.65763   22.72294
## 2016-04-07 10:00:00 CST  46.05991 -0.049235542   20.65645   21.28935
## 2016-04-07 14:00:00 CST  46.68220  0.100136663   21.71865   21.62580
## 2016-04-07 18:00:00 CST  44.35104 -0.099562259   20.29023   17.75060
## 2016-04-07 22:00:00 CST  47.52344  0.581704813   22.94151   21.44666
##                         Hospital13 Hospital14 Hospital16 Hospital17
## 2016-04-07 2:00:00 CST    14.18914   32.17005   10.82161   5.461864
## 2016-04-07 6:00:00 CST    16.98470   34.75889   13.66608   7.405919
## 2016-04-07 10:00:00 CST   15.60828   32.78517   12.15655   6.132014
## 2016-04-07 14:00:00 CST   16.09937   33.90180   12.90054   6.627555
## 2016-04-07 18:00:00 CST   13.10030   31.31449   10.09999   4.549515
## 2016-04-07 22:00:00 CST   16.10017   35.22103   13.59852   7.269719
##                         Hospital22 Hospital35 Hospital40
## 2016-04-07 2:00:00 CST    67.97906   125.9879   7.900088
## 2016-04-07 6:00:00 CST    71.17072   128.6981  10.118229
## 2016-04-07 10:00:00 CST   68.29214   126.7926   8.907765
## 2016-04-07 14:00:00 CST   68.98362   127.3150   9.436575
## 2016-04-07 18:00:00 CST   67.19249   125.1709   7.195305
## 2016-04-07 22:00:00 CST   70.91280   128.5247  10.100224
#使用dygraphs套件做可視化處理
library(dygraphs)

Time.plot = dygraph(Total.data)
Time.plot = dyOptions(Time.plot,stackedGraph = TRUE)
Time.plot = dyRangeSelector(Time.plot,height = 50)
Time.plot  #展示Time.plot
#使用htmlwidgets套件儲存結果
library(htmlwidgets)
saveWidget(Time.plot, "Timeplot.html")

第三節:Case study-KAMERA data(8)

– 另外,為了增加WebApp的華麗程度,請先下載shinydashboard套件,讓我們能使用他的ui模板

library(shiny)
library(shinydashboard)
library(e1071)
library(DT)
library(dygraphs)
library(htmlwidgets)

dashboardPage( 
  skin="yellow",
  dashboardHeader(
    title="Emergency Room Demand Forecast",
    titleWidth = 450
  ),
  dashboardSidebar(
    sidebarMenu(
      menuItem("Time series plot", tabName = "PLOT", icon = icon("dashboard")),
      menuItem("Raw data", tabName = "RAW", icon = icon("th")),
      fluidRow(
        column(12, align="center",
               h2(p(strong(span(style="color:white","Date Range")))),
               dateRangeInput("dates", label = "", start = Sys.Date(), end = Sys.Date()+6, min = Sys.Date(), max = Sys.Date()+100),
               actionButton("submit",strong("Start to predict!"),icon("list-alt")),
               tags$style(type='text/css', "#submit { vertical-align: middle; height: 40px; width: 85%; font-size: 20px;}")
        )
      )
    )
  ),
  dashboardBody(
    tabItems(
      tabItem(tabName = "PLOT",
              dygraphOutput("timeplot", width = "100%", height = "800px"),
              downloadButton("download1", label = "Download HTML plot", class = NULL)
      ),
      tabItem(tabName = "RAW",
              DT::dataTableOutput('table'),
              downloadButton("download2", label = "Download raw data", class = NULL)
      )
    )
  )
)
library(shiny)
library(shinydashboard)
library(e1071)
library(DT)
library(dygraphs)
library(htmlwidgets)

load("svmmodel.RData")  #重新載入final.model
load("predict.RData")  #重新載入predict.data
#註:如果將.RData檔案放置在WebApp的資料夾內,則只需要使用 load("svmmodel.RData") 即可

shinyServer(function(input, output) {
  
  TOTAL.DATA = eventReactive(input$submit,{
    
    #自訂欲預測之日期範圍,並產生demo.data
    start.date = as.Date(input$dates[1])
    end.date = as.Date(input$dates[2])
    new.data = data.frame(date = seq(start.date, end.date, by=1))
    new.data$x2 = weekdays(new.data$date) 
    demo.data = merge(predict.data, new.data, by = "x2")
    
    #使用final.model對demo.data進行預測
    demo.data$y = predict(final.model, demo.data[,1:3]) 
    
    TimeTable = table(demo.data[,c(4,2)])             #建立時間表格
    HosTable = table(demo.data[,3])                 #建立醫院表格
    Time.series= paste0(seq(2,22,by=4),":00:00 CST")  #將時段轉換為時間中位數
    
    #產生Total.data儲存結果
    Total.data = matrix(NA, ncol = length(HosTable), nrow = length(TimeTable))
    colnames(Total.data) = paste0("Hospital",names(HosTable))
    rownames(Total.data) = 1:nrow(Total.data)
    
    #使用一個三層迴圈將Total.data填滿(使用進度條監測運算時間)
    withProgress(message = "In processing...",value=0,{
      for (i in 1:nrow(TimeTable)) {
        for (j in 1:ncol(TimeTable)) {
          for (k in 1:length(HosTable)) {
            n = demo.data$y[demo.data$date==rownames(TimeTable)[i]&demo.data$x1==colnames(TimeTable)[j]&demo.data$x3==names(HosTable)[k]]
            Total.data[(i-1)*ncol(TimeTable)+j,k] = n
            incProgress(1/(ncol(TimeTable)*nrow(TimeTable)*length(HosTable)))
          }
          rownames(Total.data)[(i-1)*ncol(TimeTable)+j] = paste(rownames(TimeTable)[i],Time.series[j],sep=" ")
        }
      }
    })
    
    #回傳Total.data
    return(Total.data)
  })
  
  TIME.PLOT = reactive({
    Total.data = TOTAL.DATA()
    if (!is.null(Total.data)) {
      Time.plot = dygraph(Total.data)
      Time.plot = dyOptions(Time.plot,stackedGraph = TRUE)
      Time.plot = dyRangeSelector(Time.plot,height = 50)
      return(Time.plot)  #返回Time.plot
    }
  })
  
  output$timeplot = renderDygraph({
    TIME.PLOT()
  })
  
  output$download1 = downloadHandler(
    filename = function() {'Timeplot.html'},
    content = function(con) {
      Time.plot = TIME.PLOT()
      saveWidget(Time.plot, con)
    }
  )
  
  output$table = DT::renderDataTable({
    dat = TOTAL.DATA()
    if (!is.null(dat)) {
      DT::datatable(dat, selection="none")
    }
  })
  
  output$download2 = downloadHandler(
    filename = function() {'Rawdata.csv'},
    content = function(con) {
      write.csv(TOTAL.DATA(), con, quote = FALSE)
    }
  )
  
})

練習-3

– 你可能需要先利用下列程式碼,匯出使用對數轉化後的SVM model

– 另外,Prediction data可以用一樣的,但要注意預測完的y要記得做指數轉化回來唷

– 如果可以的話,利用radioButtons()&conditionalPanel(),讓使用者能選擇使用Model 1或Model 2(有空再練習)

library(e1071)
svm.data$y = log(svm.data$y+1)  
final.model2 = svm(y~., data = svm.data)          #利用svm.data(原始資料)內所有變數對y做預測
save(final.model2, file = "svmmodel2.RData")              #儲存預測模型至svmmodel2.RData

– ui.R

library(shiny)
library(survival)
library(googleVis)

fluidPage(
  sliderInput("Age", "Please enter your age", min=40, max=80, value=50),
  radioButtons("rx", "Please select a treatment group", c("1","2")),
  htmlOutput("chart1")
)

– server.R

library(shiny)
library(survival)
library(googleVis)

######################################################
# 就是這個部份太多餘,請簡化這個部分

data(ovarian)   
dat = ovarian 
model <- coxph(Surv(futime, fustat) ~ age + rx, data = dat) 
h0.hazard = basehaz(model,centered=FALSE) 
h0.hazard$hazard.lag = c(0,h0.hazard$hazard[-nrow(h0.hazard)])
h0.hazard$hazard.dif = h0.hazard$hazard-h0.hazard$hazard.lag
h0.hazard = h0.hazard[,c(2,4)]
h0.hazard.extend = data.frame(time = 0:max(h0.hazard[,1]), hazard.dif = 0) 
h0.hazard = h0.hazard[h0.hazard[,2]!=0,]
h0.hazard = rbind(h0.hazard.extend,h0.hazard)
h0.hazard = h0.hazard[order(h0.hazard[,1]),]
matrix.coef = matrix(model$coef,nrow=length(model$coef),ncol=1)

######################################################

shinyServer(function(input, output, session) {
  
  output$chart1<- renderGvis({
    NEW <- as.matrix(data.frame(age=input$Age,rx=as.numeric(input$rx))) #年齡=input$Age;組別=as.numeric(input$rx)
    
    indv.hazardratio = exp(NEW%*%matrix.coef) 
    indv.hazard = indv.hazardratio*h0.hazard$hazard.dif
    indv.cumhazard = cumsum(indv.hazard)
    indv.cumrate = exp(-indv.cumhazard)
    Predic.Survival = data.frame(time = h0.hazard$time, rate = indv.cumrate) 
    Predic.Survival[,2] = round(Predic.Survival[,2]*100,2)
    
    Scatter <- gvisScatterChart(Predic.Survival, 
                                options=list(
                                  explorer="{actions: ['dragToZoom', 
                                  'rightClickToReset'],
                                  maxZoomIn:0.05}",
                                  legend="none",
                                  lineWidth=2, pointSize=0,
                                  vAxis="{title:'Survival (%)'}",
                                  vAxes="[{viewWindowMode:'explicit',
                                  viewWindow:{min:0, max:100}}]",
                                  hAxis="{title:'Time (days)'}", 
                                  colors="['#ff0000']",
                                  width=800, height=500))
    Scatter
    })
  
})

練習-3 答案(更換SVM模型)

library(shiny)
library(shinydashboard)
library(e1071)
library(DT)
library(dygraphs)
library(htmlwidgets)

dashboardPage( 
  skin="yellow",
  dashboardHeader(
    title="Emergency Room Demand Forecast",
    titleWidth = 450
  ),
  dashboardSidebar(
    sidebarMenu(
      menuItem("Time series plot", tabName = "PLOT", icon = icon("dashboard")),
      menuItem("Raw data", tabName = "RAW", icon = icon("th")),
      fluidRow(
        column(12, align="center",
               h2(p(strong(span(style="color:white","Date Range")))),
               dateRangeInput("dates", label = "", start = Sys.Date(), end = Sys.Date()+6, min = Sys.Date(), max = Sys.Date()+100),
               actionButton("submit",strong("Start to predict!"),icon("list-alt")),
               tags$style(type='text/css', "#submit { vertical-align: middle; height: 40px; width: 85%; font-size: 20px;}")
        )
      )
    )
  ),
  dashboardBody(
    tabItems(
      tabItem(tabName = "PLOT",
              dygraphOutput("timeplot", width = "100%", height = "800px"),
              downloadButton("download1", label = "Download HTML plot", class = NULL)
      ),
      tabItem(tabName = "RAW",
              DT::dataTableOutput('table'),
              downloadButton("download2", label = "Download raw data", class = NULL)
      )
    )
  )
)
library(shiny)
library(shinydashboard)
library(e1071)
library(DT)
library(dygraphs)
library(htmlwidgets)

load("svmmodel2.RData")    #載入final.model2
load("predict.RData")

shinyServer(function(input, output) {
  
  TOTAL.DATA = eventReactive(input$submit,{
    
    start.date = as.Date(input$dates[1])
    end.date = as.Date(input$dates[2])
    new.data = data.frame(date = seq(start.date, end.date, by=1))
    new.data$x2 = weekdays(new.data$date) 
    demo.data = merge(predict.data, new.data, by = "x2")
    
    demo.data$y = predict(final.model2, demo.data[,1:3]) #請將final.model改成final.model2
    demo.data$y = exp(demo.data$y)-1                     #請對demo.data$y做指數轉換
    
    TimeTable = table(demo.data[,c(4,2)])            
    HosTable = table(demo.data[,3])                 
    Time.series= paste0(seq(2,22,by=4),":00:00 CST") 
    
    Total.data = matrix(NA, ncol = length(HosTable), nrow = length(TimeTable))
    colnames(Total.data) = paste0("Hospital",names(HosTable))
    rownames(Total.data) = 1:nrow(Total.data)
    
    withProgress(message = "In processing...",value=0,{
      for (i in 1:nrow(TimeTable)) {
        for (j in 1:ncol(TimeTable)) {
          for (k in 1:length(HosTable)) {
            n = demo.data$y[demo.data$date==rownames(TimeTable)[i]&demo.data$x1==colnames(TimeTable)[j]&demo.data$x3==names(HosTable)[k]]
            Total.data[(i-1)*ncol(TimeTable)+j,k] = n
            incProgress(1/(ncol(TimeTable)*nrow(TimeTable)*length(HosTable)))
          }
          rownames(Total.data)[(i-1)*ncol(TimeTable)+j] = paste(rownames(TimeTable)[i],Time.series[j],sep=" ")
        }
      }
    })
    
    return(Total.data)
  })
  
  TIME.PLOT = reactive({
    Total.data = TOTAL.DATA()
    if (!is.null(Total.data)) {
      Time.plot = dygraph(Total.data)
      Time.plot = dyOptions(Time.plot,stackedGraph = TRUE)
      Time.plot = dyRangeSelector(Time.plot,height = 50)
      return(Time.plot) 
    }
  })
  
  output$timeplot = renderDygraph({
    TIME.PLOT()
  })
  
  output$download1 = downloadHandler(
    filename = function() {'Timeplot.html'},
    content = function(con) {
      Time.plot = TIME.PLOT()
      saveWidget(Time.plot, con)
    }
  )
  
  output$table = DT::renderDataTable({
    dat = TOTAL.DATA()
    if (!is.null(dat)) {
      DT::datatable(dat, selection="none")
    }
  })
  
  output$download2 = downloadHandler(
    filename = function() {'Rawdata.csv'},
    content = function(con) {
      write.csv(TOTAL.DATA(), con, quote = FALSE)
    }
  )
  
})

練習-3 答案(簡化上週習題)

library(survival)
data(ovarian)   
dat = ovarian 
model <- coxph(Surv(futime, fustat) ~ age + rx, data = dat) 
h0.hazard = basehaz(model,centered=FALSE) 
h0.hazard$hazard.lag = c(0,h0.hazard$hazard[-nrow(h0.hazard)])
h0.hazard$hazard.dif = h0.hazard$hazard-h0.hazard$hazard.lag
h0.hazard = h0.hazard[,c(2,4)]
h0.hazard.extend = data.frame(time = 0:max(h0.hazard[,1]), hazard.dif = 0) 
h0.hazard = h0.hazard[h0.hazard[,2]!=0,]
h0.hazard = rbind(h0.hazard.extend,h0.hazard)
h0.hazard = h0.hazard[order(h0.hazard[,1]),]
matrix.coef = matrix(model$coef,nrow=length(model$coef),ncol=1)
save(h0.hazard, matrix.coef, file = "matrices.RData")              #儲存預測模型至matrices.RData
library(shiny)
library(googleVis)

fluidPage(
  sliderInput("Age", "Please enter your age", min=40, max=80, value=50),
  radioButtons("rx", "Please select a treatment group", c("1","2")),
  htmlOutput("chart1")
)
library(shiny)
library(googleVis)

load("matrices.RData")   #載入h0.hazard & matrix.coef

shinyServer(function(input, output, session) {
  
  output$chart1<- renderGvis({
    NEW <- as.matrix(data.frame(age=input$Age,rx=as.numeric(input$rx))) #年齡=input$Age;組別=as.numeric(input$rx)
    
    indv.hazardratio = exp(NEW%*%matrix.coef) 
    indv.hazard = indv.hazardratio*h0.hazard$hazard.dif
    indv.cumhazard = cumsum(indv.hazard)
    indv.cumrate = exp(-indv.cumhazard)
    Predic.Survival = data.frame(time = h0.hazard$time, rate = indv.cumrate) 
    Predic.Survival[,2] = round(Predic.Survival[,2]*100,2)
    
    Scatter <- gvisScatterChart(Predic.Survival, 
                                options=list(
                                  explorer="{actions: ['dragToZoom', 
                                  'rightClickToReset'],
                                  maxZoomIn:0.05}",
                                  legend="none",
                                  lineWidth=2, pointSize=0,
                                  vAxis="{title:'Survival (%)'}",
                                  vAxes="[{viewWindowMode:'explicit',
                                  viewWindow:{min:0, max:100}}]",
                                  hAxis="{title:'Time (days)'}", 
                                  colors="['#ff0000']",
                                  width=800, height=500))
    Scatter
  })
  
})

網頁應用程式總結

– 關於使用shiny套件的學習資源,可以參考shiny的官方網站

– 若是你想要加強R的可視化處理,我推薦htmlwidgets for R以及googleVis Tutorial

– 但注意,免費帳戶每月僅能讓App運作25小時,並且只能上傳5個App

– 除此之外,如果你的原始碼有重要的商業價值,建議還是自建server

分享你的App至shinyapps.io

install.packages("devtools")
library(devtools)
devtools::install_github('rstudio/shinyapps')
library(shinyapps)

建立R與你的帳戶的聯結

F16_1

F16_2

F16_3

分享

– 點選Publish後,會出現個小視窗,指定檔名後(這也是你未來的網址名稱)就可以上傳至shinyapps.io了

F16_4